package thread

import (
	"codejy/store"
	"codejy/structdef"
	"codejy/typedefault"
	"codejy/utils"
	"strconv"
	"strings"
	"sync"
	"time"
)

// key 实例地址,value 值

type ThreadTool struct {
	//等待队列
	Addr        string
	ThreadStore *structdef.ThreadStore
	WaitQueue   map[string]chan int
	WaitGroup   utils.SyncMap[string, sync.WaitGroup] //线程组名称，线程数量
}

func (f *ThreadTool) CreateTypeDefault(addr string, threadStore *structdef.ThreadStore, parameter ...string) {
	fUtil := &ThreadTool{
		Addr:        addr,
		ThreadStore: threadStore,
		WaitQueue:   make(map[string]chan int),
	}
	store.AddTypeDefaultInstanceStore(threadStore, f.GetTypeName(), addr, fUtil)
}
func (f *ThreadTool) GetData() interface{} {
	return nil
}

func (a *ThreadTool) ToStr() string {
	return ""
}
func (a *ThreadTool) ParameterProcessing(parameter []string) []string {
	//排除前后""
	for i := range parameter {
		s := parameter[i]
		if strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") {
			s = s[1 : len(s)-1]
			parameter[i] = s
		}
	}
	return parameter
}

// 停止线程
func (f *Thread) StopThread(threadId string) {
	//结束指定线程内部正在执行的函数,原理就是让线程内部的函数自己结束
	store.GetThreadStore(threadId).ThreadStopFlag = true
}

// 当前线程等待
func (a *ThreadTool) Wait(threadId string) {
	//创建一个通道
	a.WaitQueue[threadId] = make(chan int)
	//阻塞当前线程,等待被唤醒
	<-a.WaitQueue[threadId]
	// 被唤醒后,通知唤醒线程,我已经被唤醒了
	a.WaitQueue[threadId] <- 2

}

// 唤醒目标线程
func (a *ThreadTool) Wake(threadId string) {
	_, p := a.WaitQueue[threadId]
	if !p {
		//循环等待3秒,避免唤醒线程在目标阻塞线程前执行导致唤醒失败
		for i := 0; i < 300; i++ {
			time.Sleep(time.Millisecond * 10) //睡眠10毫秒
			_, p = a.WaitQueue[threadId]
			if p {
				break
			}
		}
		//如果在3秒内目标线程还是没有使用wait,则报错表示目标线程逻辑有问题
		if !p {
			panic("错误::::::目标线程并没有使用wait,请检查目标线程逻辑是否有问题,线程id:" + threadId)
		}
	}
	//通知目标线程,我要唤醒你了
	a.WaitQueue[threadId] <- 1
	//持续3秒如果没有收到确认消息,则唤醒失败
	select {
	case <-a.WaitQueue[threadId]:
		break
	case <-time.After(time.Second * 3):
		panic("错误::::::唤醒线程失败,线程id:" + threadId)

	}
	//删除通道
	delete(a.WaitQueue, threadId)
}

// 睡眠sleep ,单位毫秒
func (f *ThreadTool) Sleep(timeNum string) {
	//字符串转数字
	atoi, err := strconv.ParseInt(timeNum, 10, 64)
	if err != nil {
		panic("错误::::::sleep函数的参数必须是数字,单位毫秒")
	}
	time.Sleep(time.Duration(atoi) * time.Millisecond)
}

// 发送消息到指定线程
func (f *ThreadTool) SendMsg(threadId string, msg string) {
	store.GetThreadStore(threadId).ThreadChan <- msg
}

// 接收消息
func (f *ThreadTool) TakeMsg(threadId string) string {
	return <-store.GetThreadStore(threadId).ThreadChan
}

// 创建线程阻塞 ，等待一组线程执行完毕
func (f *ThreadTool) CreateThreadWaitGroup(groupName string, size string) {
	//字符串转数字
	atoi, err := strconv.Atoi(size)
	if err != nil {
		panic("错误::::::CreateThreadWaitGroup函数的参数必须是数字")
	}
	f.WaitGroup.Add(groupName, sync.WaitGroup{})
	get, b := f.WaitGroup.Get(groupName)
	if !b {
		panic("错误::::::CreateThreadWaitGroup函数的参数groupName已经存在")
	}
	get.Add(atoi)

}

// Done
func (f *ThreadTool) Done(groupName string) {
	get, b := f.WaitGroup.Get(groupName)
	if !b {
		panic("错误::::::Done函数的参数groupName不存在")
	}
	get.Done()
}

func (f *ThreadTool) GetDef() string {
	return `
		@CodeManual(def="ThreadTool.xxx",des="线程操作,工具类")
		type share ThreadTool{
			def init{
				core&CreateTypeDefault()
			}
			@CodeManual(def="ThreadTool.sleep(毫秒数)",des="线程睡眠,单位毫秒")
			def sleep{
				core&Sleep($1)
			}
			
			@CodeManual(def="ThreadTool.wait()",des="线程等待,当前线程等待,直到被唤醒")
			def wait{
				core&Wait(threadId)
			}
			@CodeManual(def="ThreadTool.wake($1)",des="线程唤醒,唤醒目标线程,$1=目标线程id")
			def wake{
				core&Wake($1)
			}

			@CodeManual(def="ThreadTool.send($1,$2)",des="发送消息到指定线程,$1=目标线程id,$2=内容")
			def send{
				core&SendMsg($1,$2)
			}

			@CodeManual(def="ThreadTool.take()",des="接收其他线程发送的消息")
			def take{
				to core&TakeMsg(threadId)
			}

			@CodeManual(def="stop(线程id)",des="停止一个子线程,需要传入线程id")
			def stop{
				core&StopThread($1)
			}

		}
	`
}
func init() {
	typedefault.RegisterTypeDefault(&ThreadTool{})
}
func (f *ThreadTool) GetTypeName() string {
	return "ThreadTool"
}
